using System;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using log4net;
using System.Text;
using CodeMadeSimple.Common.Exceptions;
using CodeMadeSimple.Common.Validation;
using CodeMadeSimple.Xml;

namespace EnvRide.Core.ObjectModel
{
    /// <summary>
    /// Base class for overrides.
    /// </summary>
    public abstract class Override : IXmlSerializable, IValidatable
    {
        #region Fields

        private string _xpath;
        private string _name;
        private string _if;
        private Environment _environment;

        #endregion

        #region Properties

        /// <summary>
        /// Override name.
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Xpath expression.
        /// </summary>
        public string Xpath
        {
            get { return _xpath; }
            set { _xpath = value; }
        }

        /// <summary>
        /// A csharp expression that should evaluate to a boolean value indicating if the include should be processed or not.
        /// </summary>
        public string If
        {
            get { return _if; }
            set { _if = value; }
        }
        
        /// <summary>
        /// Environment that owns this override.
        /// </summary>
        [XmlIgnore]
        public Environment Environment
        {
            get { return _environment; }
            set { _environment = value; }
        }
        
        #endregion

        #region Methods

        /// <summary>
        /// Returns the default name used when the actual Name is null or empty.
        /// </summary>
        /// <returns>Default name.</returns>
        protected abstract string GetDefaultName();

        /// <summary>
        /// Process the override with the specified configuration file.
        /// </summary>
        /// <param name="args">Process arguments.</param>
        public void Process(EnvRideProcessArguments args)
        {
            if (args == null)
                throw new ArgumentNullException("args");

            if (args.Document == null)
                throw new NullReferenceException("The document to process its null");

            using (ThreadContext.Stacks[EnvRideProject.ENV_RIDE_LOG_STACK].Push(this.ToString()))
            {
                if (Environment == null)
                    throw new NullReferenceException("The Environment property its null!");

                if (args.ParseIfExpression(If))
                {
                    if (EnvRideProject.Logger.IsDebugEnabled)
                        EnvRideProject.Logger.Debug("Processing...");

                    // Retrieves the nodes 
                    XmlNodeList nodes = XmlUtils.LookForNodes(args.Document, Xpath, false);

                    if (nodes != null && nodes.Count > 0)
                    {
                        foreach (XmlNode node in nodes)
                        {
                            if (!(node is XmlElement))
                            {
                                EnvRideProject.Logger.ErrorFormat("Node \"{0}\" its not an element!", XmlUtils.GetFullPath(node));
                                args.HasErrors = true;
                            }

                            // Process the override
                            Process(args, (XmlElement)node);
                        }
                    }
                    else
                    {
                        EnvRideProject.Logger.ErrorFormat("The XPath \"{0}\" did not return any nodes!", Xpath);

                        args.HasErrors = true;
                    }
                }
            }
        }

        /// <summary>
        /// Implements the override.
        /// </summary>
        /// <param name="args">Process arguments.</param>
        /// <param name="elem">Element to process.</param>
        protected abstract void Process(EnvRideProcessArguments args, XmlElement elem);

        public override string ToString()
        {
            if (!String.IsNullOrEmpty(Name))
                return Name;
            else
                return GetDefaultName();
        }

        #endregion

        #region IXmlSerializable Members

        public XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public virtual void ReadXml(XmlReader reader)
        {
            Name = reader.GetAttribute("name");
            Xpath = reader.GetAttribute("xpath");
            If = reader.GetAttribute("if");
        }

        public virtual void WriteXml(XmlWriter writer)
        {
            if (!String.IsNullOrEmpty(Name))
                writer.WriteAttributeString("name", Name);

            if (!String.IsNullOrEmpty(Xpath))
                writer.WriteAttributeString("xpath", Xpath);

            if (!String.IsNullOrEmpty(If))
                writer.WriteAttributeString("if", If);
        }

        #endregion

        #region IValidatable Members

        public string GetValidationId()
        {
            return ToString();
        }

        public ValidationResult Validate()
        {
            ValidationResult result = new ValidationResult(this);

            if (String.IsNullOrEmpty(Xpath))
            {
                result.AddMessage(new RequiredValueValidationMessage("Xpath"));
            }

            AddValidationMessages(result);

            return result;
        }

        protected virtual void AddValidationMessages(ValidationResult result)
        {

        }

        #endregion
    }
}